﻿using System;
using System.Collections.Generic;
using System.Data;

namespace FluentSql
{
    public class ReaderFluentSqlCommand<T> : BaseFluentSqlCommand, IReaderFluentSqlCommand<T>
    {
        #region Public Constructors

        public ReaderFluentSqlCommand()
        {
            Behavior = CommandBehavior.Default;
            Caching = CachingMode.Standard;
        }

        #endregion Public Constructors

        #region Public Properties

        public CommandBehavior Behavior { get; set; }

        public CachingMode Caching { get; set; }

        public Func<T> InitResult { get; set; }

        #endregion Public Properties

        #region Public Methods

        public T ExecuteReader(params Action<IDalSqlDataReader, T>[] iDeserialize)
        {
            if (KeepConnection)
            {
                return ExecuteReaderImpl(Context.PersistentConnection, iDeserialize);
            }
            else
            {
                using (var connection = Context.CreateConnection())
                {
                    return ExecuteReaderImpl(connection, iDeserialize);
                }
            }
        }

        public T ExecuteReaderImpl(IDalSqlConnection iConnection, params Action<IDalSqlDataReader, T>[] iDeserialize)
        {
            using (var transaction = iConnection.BeginTransaction(IsolationLevel))
            {
                T result = InitResult();
                using (var command = transaction.CreateCommand(CommandType, Command))
                {
                    SerializeParameters?.Invoke(command);
                    using (var reader = command.ExecuteReader(Behavior, Caching))
                    {
                        var i = 0;
                        var serialize = iDeserialize[i];
                        while (reader.Read())
                        {
                            serialize(reader, result);
                        }
                        while (reader.NextResult())
                        {
                            i++;
                            serialize = iDeserialize[i];
                            while (reader.Read())
                            {
                                serialize(reader, result);
                            }
                        }
                        reader.Close();
                    }
                }
                transaction.Commit();
                return result;
            }
        }

        public IEnumerable<T> ExecuteReaderYield(params Func<IDalSqlDataReader, T, T>[] iDeserialize)
        {
            if (KeepConnection)
            {
                return ExecuteReaderYieldImpl(Context.PersistentConnection, iDeserialize);
            }
            else
            {
                using (var connection = Context.CreateConnection())
                {
                    return ExecuteReaderYieldImpl(connection, iDeserialize);
                }
            }
        }

        public IEnumerable<T> ExecuteReaderYieldImpl(IDalSqlConnection iConnection, params Func<IDalSqlDataReader, T, T>[] iDeserialize)
        {
            using (var transaction = iConnection.BeginTransaction(IsolationLevel))
            {
                T result = InitResult();
                using (var command = transaction.CreateCommand(CommandType, Command))
                {
                    SerializeParameters(command);
                    using (var reader = command.ExecuteReader(Behavior, Caching))
                    {
                        var i = 0;
                        var serialize = iDeserialize[i];
                        while (reader.Read())
                        {
                            yield return serialize(reader, result);
                        }
                        while (reader.NextResult())
                        {
                            i++;
                            serialize = iDeserialize[i];
                            while (reader.Read())
                            {
                                yield return serialize(reader, result);
                            }
                        }
                        reader.Close();
                    }
                }
                transaction.Commit();
            }
        }

        public IReaderFluentSqlCommand<T> SetCachingMode(CachingMode iCachingMode)
        {
            Caching = iCachingMode;
            return this;
        }

        public IReaderFluentSqlCommand<T> SetBehavior(CommandBehavior iBehavior)
        {
            Behavior = iBehavior;
            return this;
        }

        public IReaderFluentSqlCommand<T> SetInitResult(Func<T> iInitResult)
        {
            InitResult = iInitResult;
            return this;
        }

        public IReaderFluentSqlCommand<T> SetCommand(string iCommand)
        {
            SetCommandImpl(iCommand);
            return this;
        }

        public IReaderFluentSqlCommand<T> SetCommandType(CommandType iCommandType)
        {
            SetCommandTypeImpl(iCommandType);
            return this;
        }

        public IReaderFluentSqlCommand<T> SetContext(IDalContext iContext)
        {
            SetContextImpl(iContext);
            return this;
        }

        public IReaderFluentSqlCommand<T> SetIsolationLevel(IsolationLevel iIsolationLevel)
        {
            SetIsolationLevelImpl(iIsolationLevel);
            return this;
        }

        public IReaderFluentSqlCommand<T> SetKeepConnection(bool iKeepConnection)
        {
            SetKeepConnectionImpl(iKeepConnection);
            return this;
        }

        public IReaderFluentSqlCommand<T> SetParameters(Action<IDalSqlCommand> iSerializeParameters)
        {
            SetParameters(iSerializeParameters);
            return this;
        }

        #endregion Public Methods
    }
}
